
/**********************************/
/* Module R 5 - Serial Port
  Group 3
  Nov 11, 2008
*/
/***********************************/

#include <stdio.h>
#include <string.h>
#include <dos.h>
#include "mpx_supt.h"


#define RINGBUFFERSIZE 100

extern char p_msg[80];
extern int p_length;

/* define DCB structure */
struct DCB
{
       int state; //Allocation Status of the DCB
       int flag; //port flag
     int*  e_flag; //event flag

     char* in_buffer;  /* the buffer  */
     int* in_count;  /*  counter for number of bytes in the buffer */
     int in_done; /*counter for how many characters have been processed */


      char* out_buffer;  /* the buffer  */
    int* out_count;  /*  counter for number of bytes in the buffer */
      int out_done; /*counter for how many characters have been processed */


char  ringbuffer[RINGBUFFERSIZE];
int ringbuf_in;
int ringbuf_out;
int ringbuf_count;


} pDCB;

//mpx_dcb pDCB;


#define OPEN 1
#define CLOSE 0


#define PIC_MASK 0x21
#define PIC_CMD 0x20
#define EOI 0x20

#define COM1_INT_ID 0x0C
#define COM2_INT_ID 0x0B

#define COM1_BASE 0x3F8
#define COM2_BASE 0x2F8

#define COM1_INT_EN (COM1_BASE + 1)
#define COM1_BRD_LSB COM1_BASE
#define COM1_BRD_MSB (COM1_BASE + 1)
#define COM1_INT_ID_REG (COM1_BASE + 2)
#define COM1_LC (COM1_BASE + 3)
#define COM1_MC (COM1_BASE + 4)
#define COM1_LS (COM1_BASE + 5)
#define COM1_MS (COM1_BASE + 6)


int com_open(int *eflag_p, int baud_rate);
int com_close(void);
int com_read(char *buf_p, int *count_p);
int com_write(char *buf_p, int *count_p);

void interrupt detectHandler(void);
void inputHandler(void);
void outputHandler(void);
void interrupt (*oldHandler)(void);



int com_open(int *eflag_p, int baud_rate)
{        int mask;
       if(eflag_p == NULL)
               return -101;
       if(baud_rate <= 0)
               return -102;
       if(pDCB.flag==OPEN)
               return -103;
       else
       {    /* DCB initializtion */
           int baud_rate_divisor;

           pDCB.state = IDLE;
           pDCB.flag = OPEN;
           pDCB.e_flag = eflag_p;

         pDCB.ringbuf_in=0;
         pDCB.ringbuf_out=0;
         pDCB.ringbuf_count=0;

            oldHandler = getvect(COM1_INT_ID);
            setvect (COM1_INT_ID,&detectHandler);

               baud_rate_divisor = 115200/(long) baud_rate;
               outportb(COM1_LC, 0x80);
               outportb(COM1_BRD_LSB ,baud_rate_divisor & 0xFF);
               outportb(COM1_BRD_MSB ,(baud_rate_divisor  >> 8) & 0xFF);
               outportb(COM1_LC, 0x03);

               disable();
               mask = inportb(PIC_MASK);
               mask = mask & ~0x10;
               outportb(PIC_MASK, mask);
               enable();

           outportb(COM1_MC, 0x08);

           outportb(COM1_INT_EN, 0x01);
               //store 0x08 in modem control register to enable serial interrupt
               //store 0x01 in the interrupt enable register to enable input ready interrupts
               return 0;
       }
}

int com_close(void)
{           int mask;
       if(pDCB.flag!= OPEN)
       {
               return -201;
       }
       else
       {     pDCB.flag=CLOSE;
               disable();
               mask = inportb(PIC_MASK);
               mask = mask | 0x10;
               outportb(PIC_MASK, mask);
               enable();

               //load 0x00 into modem status and interrupt inable registers, disable ACC interrupts
               //restore original interrupts using setvect()
          outportb(COM1_MS, 0x00);
          outportb(COM1_INT_EN, 0x00);
          setvect (COM1_INT_ID,oldHandler);

            return 0;
       }
}

int com_read(char *buf_p, int *count_p)
{

       if(pDCB.flag != OPEN)
       {
               return -301;
       }

       if(buf_p == NULL)
       {
               return -302;
       }

       if(count_p == NULL)
       {
               return -303;
       }
       if(pDCB.state != IDLE)
       {
               return -304;
       }

           pDCB.in_buffer = buf_p;
           pDCB.in_count = count_p;
               pDCB.in_done = 0;

               *pDCB.e_flag = 0;
              disable();
               pDCB.state = READ;
                 

/* modified */
 while ( ( pDCB.ringbuf_count>0)&&(pDCB.in_done>=*pDCB.in_count)&&(*(pDCB.in_buffer-1)!='\r') )
   {
     *pDCB.in_buffer = pDCB.ringbuffer[pDCB.ringbuf_out];
      pDCB.in_buffer++;
      pDCB.in_done++;

      pDCB.ringbuf_out = ( pDCB.ringbuf_out+1)%RINGBUFFERSIZE;
      pDCB.ringbuf_count--;
   }
            enable();

               if(pDCB.in_done<*pDCB.in_count)
               {
                       return 0;

               }

               if (*(pDCB.in_buffer-1) == '\r')
               {
                     *(pDCB.in_buffer-1) = '\0';
               }

               else
               {
                      *(pDCB.in_buffer) = '\0';
               }
               pDCB.state = IDLE;
               *pDCB.e_flag= 1;
              *pDCB.in_count = pDCB.in_done;


 return 0;
}

int com_write(char *buf_p, int *count_p)
{
              int mask;
       if(pDCB.flag != OPEN)
       {
               return -401;
       }

       if(buf_p == NULL)
       {
               return -402;
       }

       //if((count_p == NULL)||(*count_p<=0))
       if(count_p == NULL)
     {
               return -403;
       }
       if(pDCB.state != IDLE)
       {
               return -404;
       }
       else
       {
               pDCB.out_buffer = buf_p;
               pDCB.out_count = count_p;
               pDCB.out_done =0;

               pDCB.state = WRITE;
               *pDCB.e_flag = 0;

               outportb(COM1_BASE, *pDCB.out_buffer);
               pDCB.out_buffer++;
               pDCB.out_done++;

             disable();
               mask = inportb(COM1_INT_EN);
               mask = mask | 0x02;
               outportb(COM1_INT_EN,mask);
             enable();
               return 0;
       }
}


/*****************************************************
 Function:   void interrupt detectHandler()
 Outline:
 If port is not open- clear the interrupt and return
outportb(0x20, EOI);  EOI = End of interrupt code
 Otherwise, read the Interrupt ID Register to determine the cause of the interrupt.
Get the value of the Interrupt ID Register by using inportb(INT_ID);
This will return an integer value.
 Bit 0 must be 0 if the interrupt was actually caused by the serial port.
 Bits 2 and 1 indicate the specific interrupt type:
00 = Modem Status Interrupt
01 = Output Interrupt
10  = Input Interrupt
11 = Line Status Interrupt
  Call the appropriate second-level handler
If bit2bit1 = 01, call the write second level interrupt handler
If bit2bit1 = 10, call the read second level interrupt handler
Clear the interrupt by sending EOI to the PIC Command Register (0x20)

*******************************************************/

void interrupt detectHandler(void){
       int mask;
     int i;
   if(pDCB.flag !=OPEN)
     {
     outportb(PIC_CMD, EOI);
     return;
     }


       (int)i = inportb(COM1_INT_ID_REG); /*  read interrupt ID */



 /*
    if ((i&0x07)==0x00)
      inportb(COM1_MS);
*/
      if ((i&0x07)==0x02)
        outputHandler();
      else if ((i&0x07)==0x04)
        inputHandler();
 /*
      else if ((i&0x07)==0x06)
        inportb(COM1_LS);
 */


       outportb(PIC_CMD,EOI);

}


/**************************************
Function:  void inputHandler()
Outline:
Read a character from the input register
Your variable = inportb(INPUT_REGISTER); Input register for COM1 is the base address for the port.
If current status is NOT reading
Store the character in the ring buffer.
If the buffer is full, discard the character.
Return and do not signal completion.
If current status is reading
Store the character in the input buffer
If the count is not completed and the character is not a CR
Return and do not signal completion.
Otherwise, transfer is completed.
Set status to idle
Set the event flag (set = 1)
Set input count = input done

***************************************/
void inputHandler(){
 char c;
 (char) c = inportb(COM1_BASE);

 if(pDCB.state!=READ)
 {
  if(pDCB.ringbuf_count!=RINGBUFFERSIZE)
     
  
    {
     pDCB.ringbuffer[pDCB.ringbuf_in]=c;
     pDCB.ringbuf_in = (pDCB.ringbuf_in+1)%RINGBUFFERSIZE;
     pDCB.ringbuf_count++;

    }
 }

 else
   {*pDCB.in_buffer=c;
     pDCB.in_buffer++;
     pDCB.in_done++;

   }


if  ((c=='\r')||(pDCB.in_done>*pDCB.in_count))


 {
  if (*(pDCB.in_buffer-1)=='\r')
     *(pDCB.in_buffer-1)='\0';
  
   else
      *pDCB.in_buffer='\0';

  pDCB.state= IDLE;
 *pDCB.e_flag=1;
 *(pDCB.in_count) = pDCB.in_done;
 }


}


/*************************************
Function:  void outputHandler()
Outline:
If the current status is NOT writing
Ignore the interrupt and return.
If count has not been exhausted
Get the next character from the output buffer
Store it in the output register.
Return and do not signal completion.
Otherwise, all characters have been transferred.
Reset the status to idle.
Set the event  flag (=1)
Set the count value.
Disable write interrupts by clearing bit 1 in the interrupt enable register.
mask = inportb(INTERRUPT_ENABLE);
mask = mask & ~0x02;
outportb(INTERRUPT_ENABLE,mask);
************************************/

void outputHandler(){
int mask;

if(pDCB.state!=WRITE)
 return;

if(pDCB.out_done<*pDCB.out_count)
 {
 outportb(COM1_BASE, *pDCB.out_buffer);
 pDCB.out_buffer++;
 pDCB.out_done++;
 return;
 }
 else
 {
 pDCB.state=IDLE;
 *pDCB.e_flag=1;


 disable();
 mask = inportb(COM1_INT_EN);// define INTERRUPT_ENABLE later
 mask = mask & ~0x02;
 outportb(COM1_BASE + 1,mask);
 enable();

outportb(PIC_CMD,EOI);
}
}